/*
** Copyright (c) 2008 Mobile GCalendar
**
** Permission is hereby granted, free of charge, to any person
** obtaining a copy of this software and associated documentation
** files (the "Software"), to deal in the Software without
** restriction, including without limitation the rights to use,
** copy, modify, merge, publish, distribute, sublicense, and/or sell
** copies of the Software, and to permit persons to whom the
** Software is furnished to do so, subject to the following
** conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
** OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
** HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
** OTHER DEALINGS IN THE SOFTWARE.
*/

package au.edu.mq.comp.itec800.mgc.lib;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import au.edu.mq.comp.itec800.mgc.lib.calendar.Colors;
import au.edu.mq.comp.itec800.mgc.lib.calendar.GEventManager;
import au.edu.mq.comp.itec800.mgc.util.GDataUtils;
import au.edu.mq.comp.itec800.mgc.util.StringUtils;

import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.HiddenProperty;
import com.google.gdata.data.extensions.Where;
import com.google.gdata.util.InvalidEntryException;
import com.google.gdata.util.ServiceException;

/**
 * GCalendarManager implements an IManager interface for Googgle Calendar
 * calendar objects manipulation.
 *
 * This class acts as a direct wrapper between a client-side application
 * and the GData Calendar API.
 *
 *
 * @see IManager
 *
 * @author Laurent Malvert [laurent.malvert@students.mq.edu.au]
 * @author Kenfe-Mickael Laventure [kenfe-mickael.laventure@students.mq.edu.au]
 */
public class GCalendarManager implements IManager<GCalendar>
{
  private static final Logger   logger  = Logger.getLogger(GCalendarManager.class);

  private GAccount              ga      = null;
  private GEventManager         evtMgr  = null;
  private ArrayList<GCalendar>  cals    = null;
  private CalendarEntry         curCal  = null;


  public GCalendarManager(final GAccount ga)
  {
    this.ga = ga;
    logger.info("  - instantiated GCalendarManager for GAccount [" + ga.getUser() + "]");
  }

  public GCalendar                          create(final GCalendar cal)
    throws IOException, ServiceException
  {
    return (create((CalendarEntry) cal));
  }

  public GCalendar                          create(final CalendarEntry cal)
    throws IOException, ServiceException
  {
    return (new GCalendar(getCalendarService().insert(GCFeed.owned.getFullUrl(), cal)));
  }

  public GCalendar                          create(final String title,
                                                   final String ... properties)
    throws IOException, ServiceException
  {
    final GCalendar                         cal = new GCalendar();

    if (title != null)
    {
      cal.updateName(title);
    }
    if (StringUtils.isValid(0, properties))
    {
      cal.updateTimeZone(properties[0]);
    }
    if (StringUtils.isValid(1, properties))
    {
      cal.updateVisibility(properties[1]);
    }
    if (StringUtils.isValid(2, properties))
    {
      cal.updateColor(properties[2]);
    }
    if (StringUtils.isValid(3, properties))
    {
      cal.updateLocation(properties[3]);
    }
    if (StringUtils.isValid(4, properties))
    {
      cal.updateDescription(properties[4]);
    }
    return (create(cal));
  }

  public static boolean                     delete(final GCalendar cal)
    throws IOException, ServiceException
  {
    return (delete((CalendarEntry) cal));
  }

  public static boolean                     delete(final CalendarEntry cal)
    throws IOException, ServiceException
  {
    boolean                                 status  = false;

    if (cal != null)
    {
      try
      {
        cal.delete();
        status = true;
      }
      catch (final InvalidEntryException e)
      {
        /* do nothing */
      }
    }
    return (status);
  }

  public CalendarEntry                      get(final String calId)
    throws IOException, ServiceException
  {
    return (getCalendarService().getEntry(GCFeed.getCustom(calId),
                                          CalendarEntry.class));
  }

  public CalendarEntry                      getCurrentCalendar()
  {
    return (curCal);
  }

  public void                               setCurrentCalendar(final CalendarEntry cal)
  {
    curCal    = cal;
    if (cal != null)
      evtMgr  = new GEventManager(this, cal);
    else
      evtMgr  = null;
  }

  public static boolean                     isSameCalendar(final GCalendar cal1,
                                                           final GCalendar cal2)
  {
    return ((cal1 != null) ? cal1.isSameAs(cal2) : ((cal2 != null) ? cal2.isSameAs(cal1) : false));
  }

  public static boolean                     isSameCalendar(final CalendarEntry cal1,
                                                           final CalendarEntry cal2)
  {
    final String  id1 = (cal1 != null) ? GDataUtils.getSafeId(cal1) : "";
    final String  id2 = (cal2 != null) ? GDataUtils.getSafeId(cal2) : "";

    return (id1.equals(id2));
  }

  public void                               update()
    throws IOException, ServiceException
  {
    final List<CalendarEntry>               allCal    = getAll();
    final List<CalendarEntry>               ownedCal  = getOwned();
    final ArrayList<GCalendar>              combCal   = new ArrayList<GCalendar>();

    for (final CalendarEntry acal : allCal)
    {
      boolean flag  = false;

      for (final CalendarEntry ocal : ownedCal)
      {
        if (isSameCalendar(acal, ocal))
        {
          combCal.add(new GCalendar(ocal));
          flag = true;
          break;
        }
      }
      if (flag == false)
      {
        combCal.add(new GCalendar(acal));
      }
    }
    cals = combCal;
  }

  public ArrayList<GCalendar>               getCombined()
    throws IOException, ServiceException
  {
    if (cals == null)
    {
      update();
    }
    return (cals);
  }

  public List<CalendarEntry>                getAll()
    throws IOException, ServiceException
  {
    return (GCFeed.all.getFullFeed(getCalendarService()).getEntries());
  }

  public List<CalendarEntry>                getOwned()
    throws IOException, ServiceException
  {
    return (GCFeed.owned.getFullFeed(getCalendarService()).getEntries());
  }

  public CalendarEntry                      subscribe(final CalendarEntry cal)
    throws IOException, ServiceException
  {
    return (getCalendarService().insert(GCFeed.all.getFullUrl(), cal));
  }

  public GCalendar                          subscribe(final GCalendar cal)
    throws IOException, ServiceException
  {
    return (new GCalendar(subscribe((CalendarEntry) cal)));
  }

  public GCalendar                          subscribe(final String calId)
    throws IOException, ServiceException
  {
    final GCalendar                         cal = new GCalendar();

    cal.setId(calId);
    return (subscribe(cal));
  }

  public boolean                            unsubscribe(final String calId)
    throws IOException, ServiceException
  {
    return (delete(get(calId)));
  }

  public CalendarEntry                      updateOwned(final CalendarEntry cal)
    throws IOException, ServiceException
  {
    return (getCalendarService().update(GCFeed.owned.getFullUrl(), cal));
  }

  public CalendarEntry                      updateSubscribed(final CalendarEntry cal)
    throws IOException, ServiceException
  {
    return (getCalendarService().update(GCFeed.all.getFullUrl(), cal));
  }

  public GAccount getAccount()
  {
    return (ga);
  }

  /**
   * Returns the CalendarService associated with the Google Account
   * attached to this calendar manager.
   *
   * This is just a convenience method to make the GCalendarManager
   * wrapping methods easier to browse through and read by skipping
   * one indirection.
   *
   * @return
   *          the Google Calendar Service instance.
   */
  private CalendarService                   getCalendarService()
  {
    return (getAccount().getService());
  }

  public GEventManager                      getEventManager()
  {
    return (evtMgr);
  }

  public static boolean                     isHidden(final CalendarEntry cal)
  {
    return ((cal != null) && cal.getHidden().equals(HiddenProperty.TRUE));
  }

  public static boolean                     isOwn(final CalendarEntry cal)
  {
    return ((cal != null) && (cal.getId().indexOf("/own") != -1));
  }

  public static String                      getLocation(final CalendarEntry cal)
  {
    String                                  location = "";

    if (cal != null)
    {
      final List<Where>                       locations = cal.getLocations();

      if (locations.size() != 0)
      {
        location = StringUtils.sanitize(locations.get(0).getValueString());
      }
    }
    return (location);
  }

  public static String                      getDescription(final CalendarEntry cal)
  {
    return ((cal != null) ? GDataUtils.fromTextConstruct(cal.getSummary()) : "");
  }

  public static String                      getTimeZone(final CalendarEntry cal)
  {
    return ((cal != null) ? GDataUtils.fromTimeZoneProperty(cal.getTimeZone()) : "");
  }

  public static String                      getTitle(final CalendarEntry cal)
  {
    return ((cal != null) ? GDataUtils.fromTextConstruct(cal.getTitle()) : "");
  }

  public static String                      getColor(final CalendarEntry cal)
  {
    return ((cal != null) ? Colors.fromRGB(GDataUtils.fromColorProperty(cal.getColor())).getName() : "");
  }
}
